<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="SimpleSpring开发-IOC容器的设计与实现(3), 胡言乱语">
    <meta name="description" content="MySimpleSpring自研框架IOC实现前奏[实现自研框架IOC核心功能的前置知识]工厂设计模式的思想:我们从上面通过原生JavaWeb实现业务系统的过程中，提出的需求是：需要一个自动管理类与类之间关系、自动初始化（注入）的工具
当提">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <meta name="referrer" content="no-referrer-when-downgrade">
    <!-- Global site tag (gtag.js) - Google Analytics -->


    

    <title>SimpleSpring开发-IOC容器的设计与实现(3) | 胡言乱语</title>
    <link rel="icon" type="image/x-icon, image/vnd.microsoft.icon" href="/myblog/favicon.ico">

    <link rel="stylesheet" type="text/css" href="/myblog/libs/awesome/css/all.min.css">
    <link rel="stylesheet" type="text/css" href="/myblog/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/myblog/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/myblog/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/myblog/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/myblog/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/myblog/css/my.css">

    <script src="/myblog/libs/jquery/jquery-3.6.0.min.js"></script>

<meta name="generator" content="Hexo 6.2.0"></head>


<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper container">
            <div class="brand-logo">
                <a href="/myblog/" class="waves-effect waves-light">
                    
                    <img src="/myblog/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">胡言乱语</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/myblog/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/myblog/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/myblog/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/myblog/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/myblog/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>


<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/myblog/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">胡言乱语</div>
        <div class="logo-desc">
            
            Never really desperate, only the lost of the soul.
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/myblog/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/myblog/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/myblog/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/myblog/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/myblog/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        
    </ul>
</div>


        </div>

        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/myblog/medias/featureimages/10.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <h1 class="description center-align post-title">SimpleSpring开发-IOC容器的设计与实现(3)</h1>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/myblog/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        width: 345px;
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        padding: 35px 0 15px 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content {
        padding-bottom: 30px;
        overflow: auto;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;

        position: absolute;
        right: 23.5vw;
        display: block;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/myblog/tags/%E5%90%8E%E7%AB%AF/">
                                <span class="chip bg-color">后端</span>
                            </a>
                        
                            <a href="/myblog/tags/%E9%80%A0%E8%BD%AE%E5%AD%90/">
                                <span class="chip bg-color">造轮子</span>
                            </a>
                        
                            <a href="/myblog/tags/Spring/">
                                <span class="chip bg-color">Spring</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/myblog/categories/SimpleSpring%E5%BC%80%E5%8F%91%E4%B8%93%E6%A0%8F/" class="post-category">
                                SimpleSpring开发专栏
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2022-06-04
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2022-06-04
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    7.8k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    29 分
                </div>
                

                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
        </div>
        <hr class="clearfix">

        
        <!-- 是否加载使用自带的 prismjs. -->
        <link rel="stylesheet" href="/myblog/libs/prism/prism.min.css">
        

        
        <!-- 代码块折行 -->
        <style type="text/css">
            code[class*="language-"], pre[class*="language-"] { white-space: pre-wrap !important; }
        </style>
        

        <div class="card-content article-card-content">
            <div id="articleContent">
                <h1 id="MySimpleSpring"><a href="#MySimpleSpring" class="headerlink" title="MySimpleSpring"></a>MySimpleSpring</h1><h2 id="自研框架IOC实现前奏-实现自研框架IOC核心功能的前置知识"><a href="#自研框架IOC实现前奏-实现自研框架IOC核心功能的前置知识" class="headerlink" title="自研框架IOC实现前奏[实现自研框架IOC核心功能的前置知识]"></a>自研框架IOC实现前奏[实现自研框架IOC核心功能的前置知识]</h2><h3 id="工厂设计模式的思想"><a href="#工厂设计模式的思想" class="headerlink" title="工厂设计模式的思想:"></a>工厂设计模式的思想:</h3><p>我们从上面通过原生JavaWeb实现业务系统的过程中，提出的需求是：<strong>需要一个自动管理类与类之间关系、自动初始化（注入）的工具</strong></p>
<p>当提及这样的需求时， 我们很容易想到一个设计模式：<strong>工厂模式</strong>，</p>
<p><strong>因为工厂模式最大的特点就是：可以忽略对象创建的具体细节，由工厂负责创建并返回需要的对象</strong></p>
<p>但是普通的工厂模式也有好几个，所以，我们先来分析一下，有什么普通的工厂模式符合我们的需求的：</p>
<p>（下列代码均在demo.factory包下）</p>
<ul>
<li><p>简单工厂模式：</p>
<ul>
<li><p>概念：定义一个工厂类以及多个具有共同父类或接口的产品类，客户端<strong>通过工厂类，传入不同的参数，并得到对应的产品类：</strong></p>
</li>
<li><p>大致代码思想如下：</p>
<p>​	工厂类:</p>
</li>
</ul>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528204615073.png" alt="image-20220528204615073"></p>
<p>​	    	测试: </p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528204625528.png" alt="image-20220528204625528"></p>
<ul>
<li><p>缺点:</p>
<ul>
<li><p>所有的产品类对象的创建均在一个工厂类中，如果各个对象的创建都比较复杂，该工厂类就会显得特别臃肿，违背了单一职责原则</p>
</li>
<li><p>我们会发现，该工厂类是通过类似于if-else等方式进行判断从而创建不同的产品类对象，</p>
<p>如果我们此时要多加一个产品类，那么就必须手动修改工厂类的代码才能实现新功能的添加，毫无疑问违背了开闭原则。</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>工厂方法模式：</p>
<ul>
<li><p>概念：定义多个具有共同父类或接口的工厂类 以及 多个具有共同父类或接口的产品类，</p>
<p>​		    <strong>每一个工厂类对应一个产品类，客户端通过调用不同的工厂类获得不同的产品类对象</strong></p>
</li>
<li><p>大致代码思想如下：</p>
<p>工厂类：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528205650974.png" alt="image-20220528205650974"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528205640673.png" alt="image-20220528205640673"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528205700036.png" alt="image-20220528205700036"></p>
<p>测试：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528205710652.png" alt="image-20220528205710652"></p>
</li>
<li><p>优点：</p>
<ul>
<li>把简单工厂中的单一工厂创建所有的产品类的职责下沉并分散给各个子类，一个工厂类只负责一个产品类的创建，符合单一职责</li>
<li>当新增一个产品类时，只需要新增对应的一个工厂类，然后客户端调用新工厂类即可，无需修改其他原有的代码，符合开闭原则</li>
</ul>
</li>
<li><p>缺点：</p>
<ul>
<li><p>一个产品类对应一个工厂类，产品过多时，类会太多</p>
</li>
<li><p>同一类工厂类，只能创建同一类的产品类，不能创建多种产品类</p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>抽象工厂模式：</p>
<ul>
<li><p>概念：上述的无论是简单工厂还是工厂方法模式，都是：一种工厂类对应一种产品类，一种工厂类无法创建多种产品类</p>
<p>​			<strong>而抽象工厂模式，可以实现 一个工厂类创建一系列相关的产品族， 可以认为是可以生产多种相关的产品的工厂方法模式。</strong></p>
</li>
<li><p>大致代码思想如下：</p>
<p>工厂类：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528210628738.png" alt="image-20220528210628738"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528210652414.png" alt="image-20220528210652414"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528210703388.png" alt="image-20220528210703388"></p>
<p>测试：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528210717271.png" alt="image-20220528210717271"></p>
</li>
<li><p>优点：</p>
<ul>
<li>在工程模式基础上，实现了一个工厂类可以创建一系列的产品类</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>​	至此，我们已经把普通的工厂模式相关的都说完了，那么哪个工厂模式可以实现我们的需求呢？</p>
<p>​	答案是：光靠工厂模式我们是无法实现我们的需求的</p>
<p>​	仔细想想我们的需求：我们<strong>自动管理</strong>类与类的关系，要<strong>自动</strong>实现初始化，</p>
<p>​	但是我们的<strong>这些普通的工厂模式里，都是手动写死地去new</strong>，毫无疑问是无法实现我们要求的<strong>自动</strong>的需求的，因为当我们新增一个类时，必须又得去手写实现，</p>
<p>​	注意，我们这里说的无法实现，只是<strong>特指上述我们所说的普通的工厂模式（即：简单工厂，工厂方法，抽象工厂）无法实现我们的需求，</strong></p>
<p>​	但是，毫无疑问<strong>工厂模式</strong>也将是<strong>我们要实现的功能的一个必要的设计思想</strong>：<strong>客户端只需要从工厂里面取出想要的对象，而对象的创建由工厂内部完成</strong></p>
<p>​	只不过因为普通的工厂模式的内部都是写死new来创建对象，不符合我们的自动需求罢了，</p>
<p>​	那么：<strong>我们Java有什么技术可以不写死地用new，而是动态地来创建对象呢？</strong></p>
<p>​	我们应该第一时间想到：<strong>反射！</strong></p>
<p>​	<strong>如果我们可以通过反射实现动态地创建对象，那么我们新增一个类时，就无需手写来实现其实例对象的创建，也就离我们的自动需求更近一步了！</strong></p>
<h3 id="反射："><a href="#反射：" class="headerlink" title="反射："></a><strong>反射</strong>：</h3><ul>
<li><p>概念：反射机制就有点像<strong>获得一份class字节码的抽象</strong>，我们可以通过三种方式（.class, getClass, Class.forName)获得Class对象，</p>
<p>​			并通过Class对象获得这个类其中的Field成员变量，Method成员方法，Constructor构造方法，</p>
<p>​			<strong>从而动态地去实例化对象，设置变量值，调用方法等</strong></p>
</li>
<li><p>作用:</p>
<ul>
<li><p><strong>一切都可以在运行时再进行，而非编译期就写死！</strong></p>
</li>
<li><p>在运行时，实例化一个类的对象</p>
</li>
<li><p>在运行时，访问一个对象的成员变量、成员方法等等</p>
</li>
<li><p><strong>可以实现配置化动态创建对象，即：让代码更有通用性，我们可以把一些变动的内容（例如类的全限定名称等）写在配置文件&#x2F;注解等处，</strong></p>
<p><strong>通过动态地获取配置信息，从而动态地确定要被创建的对象是什么，要调用的方法是什么等等，可以非常灵活，而原代码并未发生任何变动</strong></p>
</li>
</ul>
</li>
<li><p>具体的反射API就不在此诸多赘述了。</p>
</li>
<li><p>对实现我们要求的需求的作用：</p>
<ul>
<li><p>我们的要求是可以<strong>自动管理</strong>类与类的关系，要<strong>自动</strong>实现初始化（注入）等</p>
</li>
<li><p>而有了<strong>反射</strong>这一可以<strong>动态创建对象并访问其成员</strong> 并且 <strong>还可以实现配置化</strong> 的利器，</p>
<p>我们不就可以通过<strong>提前配置好类相关的信息</strong>，然后交由<strong>工厂</strong> 去<strong>读取这些配置，然后通过反射创建对象</strong></p>
<p>这样一来，我们的客户端就可以完全不关心这些对象的创建过程，<strong>也不需要自己手动去写new，</strong></p>
<p>只需要交由 结合了 <strong>工厂模式 + 反射</strong>的工厂，就可以获得这些对象了。</p>
<p>不就实现了我们要求的<strong>自动</strong>了吗？</p>
</li>
</ul>
<p>至此，又出现了一个新的问题，那我们<strong>如何去配置类相关的信息呢？</strong></p>
<p>借鉴一下Spring，我们可以通过类似于 <strong>xml文件 这种集中式配置的方式</strong>， 也可以 通过 <strong>注解 这种分散式配置的方式</strong></p>
<p>两种其实都可以，我们这里暂时只采用了 <strong>注解</strong> 的方式去配置，在之后有余力，可以实现一下XML的配置方式。</p>
</li>
</ul>
<p> (关于注解的相关知识，也不在此过多赘述了)</p>
<ul>
<li><p>我们一直都在说：工厂内部里要管理类与类之间的关系，<strong>那么工厂在其内部创建对象时，应该按怎么样的思想和方式去创建类对象，实现类与类的关系呢？</strong></p>
<p>例如下述例子：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528231009485.png" alt="image-20220528231009485"></p>
<ul>
<li><p>上层依赖下层的方式：</p>
<ul>
<li><p>概念：</p>
<p>我们很容易想到，既然行李箱依赖箱体，箱体依赖底盘，底盘依赖轮子，那么我们在构造方法把依赖的下层去new出来不就行了吗？</p>
<p>这就是上层依赖下层的创建方式。</p>
</li>
</ul>
<p><img src="/myblog/%5CmdImage%5Cdepend.jpg" alt="img"></p>
<ul>
<li><p>缺点：不可扩展，难以维护</p>
<p>​			如果此时我们需要修改轮子的相关信息，那么就会引起一系列的连锁反应，导致项目难以维护：</p>
<p>​			例如：我们要把Trie的构造函数改动了，那么上面使用该构造函数来new该成员变量对象的类都得改变代码：</p>
<p>​			<img src="/myblog/%5CmdImage%5CA2S%5B5%7D%7DQ%7B42MV0W_YZ5%60X2.jpg" alt="img"></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="依赖倒置原则-gt-依赖注入-gt-IOC思想-gt-IOC容器"><a href="#依赖倒置原则-gt-依赖注入-gt-IOC思想-gt-IOC容器" class="headerlink" title="依赖倒置原则 -&gt; 依赖注入 -&gt; IOC思想 -&gt; IOC容器:"></a><strong>依赖倒置原则 -&gt; 依赖注入 -&gt; IOC思想 -&gt; IOC容器:</strong></h3><ul>
<li><p>原本一般是上层依赖下层，所以：在上层的构造函数中把依赖的下层对象new出来</p>
</li>
<li><p><strong>依赖倒置</strong>的所谓倒置， 就是变为：下层依赖上层，具体实现上：把 <strong>依赖的下层对象，注入到 上层对象中</strong></p>
<p>这种实现方式，我们称为<strong>依赖注入DI： 就是把一个类的成员变量（即：依赖的对象）通过 外部注入而非内部创建 的方式来实现赋值</strong></p>
<p>由此方法，改造上面我们的那个例子：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528223705985.png" alt="image-20220528223705985"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528223727566.png" alt="image-20220528223727566"></p>
<p>此时你会发现，无论下层的怎么变，只需要改动一下该变动的对象的创建方式即可，其他的均不影响，这就解决了之前的问题。</p>
</li>
<li><p>同时，你会发现，通过<strong>依赖注入</strong>改造后的代码中，</p>
<p><strong>本来一个类对其成员变量的创建权，已经不在该类手上了，即：一个类的成员变量对象的创建，并非由其自己去实现</strong></p>
<p><strong>在此案例代码的右边，我们可以看到：对于这些对象的创建，已经统一放在了这些类的外部了</strong></p>
<p><strong>这种 把类在其内部程序本身创建对象的控制权，交由别的地方进行统一管理的设计思想，我们称为：控制反转IOC</strong></p>
</li>
<li><p>而，<strong>在此控制反转IOC的设计思想下，这个统一管理这些创建对象的地方，我们称之为：IOC容器</strong></p>
</li>
<li><p>至此，我们一一介绍了依赖倒置、依赖注入、控制反转IOC思想，IOC容器的概念，他们的关系如下：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528224407374.png" alt="image-20220528224407374"></p>
</li>
<li><p>在介绍完上述概念后，回答提出的问题：工厂内部要管理类与类之间的关系，那么<strong>工厂在内部创建对象时，应该按怎么样的思想和方式去创建类对象呢？</strong></p>
<p>答案就应该是：<strong>在依赖倒置原则 以及 控制反转设计思想的指导下，通过IOC容器，在其内部通过依赖注入的方式，实现类对象的创建</strong></p>
</li>
</ul>
<p><img src="/myblog/%5CmdImage%5DDWCM0KGIK4@CDUSGI%60ES%25F.jpg" alt="img"></p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220528225747531.png" alt="image-20220528225747531"></p>
<h3 id="总结："><a href="#总结：" class="headerlink" title="总结："></a>总结：</h3><p><img src="/myblog/%5CmdImage%5Cimage-20220528225819691.png" alt="image-20220528225819691"></p>
<p>我们依次介绍了：</p>
<ul>
<li>工厂模式 -&gt; 结论：我们要实现的<strong>自研框架要像一个工厂一样，客户端可以从该工厂里面获取对象，而无需关注内部的对象创建过程。</strong></li>
<li>反射机制 -&gt; 结论：我们要通过<strong>反射这一以实现配置化动态创建对象的特性，实现我们的自动需求</strong></li>
<li>注解 -&gt; 结论：我们要通过<strong>注解这一分散式配置方式，来为反射机制配置与提供类的相关信息。</strong></li>
<li>IOC容器 -&gt; 结论：我们的<strong>自研框架内部创建对象的方式，应该是个IOC容器</strong></li>
</ul>
<p>总结来看，要实现我们提出的<strong>需要一个自动管理类与类之间关系、自动初始化（注入）的工具</strong>的需求，</p>
<p>那<strong>我们的自研框架在这方面的实现方式</strong>就应该是：</p>
<ul>
<li><p><strong>用户可以通过 注解 来对 类的相关信息 进行配置；</strong></p>
</li>
<li><p><strong>而自研框架提供一个IOC容器，其内部的对象创建过程是：通过 读取程序中注解的配置信息+反射机制 动态地创建出对象，同时通过依赖注入的方式，实现类与类之间的依赖关系；</strong></p>
</li>
<li><p><strong>从而IOC容器就如同一个工厂一样，用户可以忽略上述的内部的对象创建过程，直接从该IOC容器中获得需要的对象</strong></p>
</li>
<li><p><strong>此过程中，用户只需要通过注解进行配置，然后就可以直接通过IOC容器获得对象，从头到尾没写过一行创建对象的代码，这就是我们的需求：“自动管理类与类之间关系、自动初始化（注入）” 的实现</strong></p>
</li>
</ul>
<h2 id="自研框架IoC容器-功能-的实现"><a href="#自研框架IoC容器-功能-的实现" class="headerlink" title="自研框架IoC容器(功能)的实现"></a>自研框架IoC容器(功能)的实现</h2><h3 id="IOC容器-功能-的实现流程"><a href="#IOC容器-功能-的实现流程" class="headerlink" title="IOC容器(功能)的实现流程:"></a>IOC容器(功能)的实现流程:</h3><ul>
<li><p>思路:</p>
<p>前面我们已经介绍过我们自研框架在实现上述需求时的实现方式了，其实该实现方式，就是<strong>IOC容器(功能)的实现</strong>！</p>
<p>据上面的分析，我们可以把框架的作用或功能分为以下几点：</p>
<ul>
<li>解析<strong>配置</strong>：</li>
<li><strong>定位到目标</strong>同时<strong>注册目标的实例对象到IOC容器</strong>内</li>
<li>实现<strong>依赖注入</strong></li>
<li>提供对外的api来对IOC容器进行操作</li>
</ul>
<p>我们要通过<strong>以下的技术选型</strong>来实现上述功能：</p>
<ul>
<li><p><strong>注解</strong>：用于给用户在特定的地方做标记进行配置，从而自研框架可以去识别并进行相应处理 &#x3D;&gt; <strong>创建注解 &#x3D;&gt; 用于配置</strong></p>
</li>
<li><p><strong>类加载器</strong>：之前学反射的时候说过，类加载器可以用于在在任何环境下获得指定范围（用户给定包名）下的所有的文件 &#x3D;&gt; <strong>用于从指定范围下获得所有的class文件，从而通过反射获得对应的Class对象</strong></p>
</li>
<li><p><strong>反射</strong>：用于通过全限定名称获得Class对象，从而获得Constructor对象进行对象实例化，获得Field对象进行注入，同时可以通过isAnnotationPresent方    法来判断是否被某注解标注 &#x3D;&gt; <strong>获得目标的对象、以及成员变量对应的Field，从而可以实现注册与注入功能</strong></p>
</li>
<li><p><strong>ConcurrentHashMap</strong>：用于作为IOC容器的载体，以Class对象作为Key（因为Class对象在JVM中只会有一个），以Class对象的实例对象作为Value &#x3D;&gt;</p>
<p><strong>实现IOC容器的载体，并被包装为一个IOC容器类，对外提供相关操作</strong></p>
</li>
</ul>
</li>
<li><p>综上，实现流程为：</p>
<ul>
<li><p>创建注解</p>
</li>
<li><p>定位与提取目标</p>
</li>
<li><p>实现容器</p>
</li>
<li><p>依赖注入</p>
</li>
</ul>
</li>
</ul>
<h3 id="创建注解："><a href="#创建注解：" class="headerlink" title="创建注解："></a>创建注解：</h3><ul>
<li><p>此处我们先来创建用于修饰类的注解 ： Controller、Service、Repository、Component</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
 * 要被注册的controller对象
 */</span>
<span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span><span class="token class-name">ElementType</span><span class="token punctuation">.</span>TYPE<span class="token punctuation">)</span> <span class="token comment">// 用于修饰类</span>
<span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span><span class="token class-name">RetentionPolicy</span><span class="token punctuation">.</span>RUNTIME<span class="token punctuation">)</span> <span class="token comment">// 要用反射进行判断,所以必须保留到Runtime时期</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@interface</span> <span class="token class-name">Controller</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
</li>
<li><p>用于修饰之前我们业务系统里的类：</p>
<p><img src="/myblog/%5CmdImage%5Cimage-20220603115015264.png" alt="image-20220603115015264"></p>
</li>
</ul>
<h3 id="定位与提取目标："><a href="#定位与提取目标：" class="headerlink" title="定位与提取目标："></a>定位与提取目标：</h3><ul>
<li><p>上面我们已经把创建好的 <strong>标识 该类的对象是要被注册到容器的类 的注解</strong> 放置到该放置的类上面了，那，<strong>我们怎么去定位并提取这些目标呢？</strong></p>
</li>
<li><p><strong>毫无疑问，要定位并提取这些目标，我们首先要实现的，就是要把用户指定范围下所有的类都给获取并创建出来！先以此得到  全部的Class对象 ，再逐一遍历去过滤从而实现定位并提取这些目标</strong></p>
</li>
<li><p>既然要实现 <strong>指定的全限定名称 -&gt; 类 -&gt; 对象</strong>   ,必然是用 <strong>反射机制</strong>，那么问题来了，当用户使用该框架提供的IOC容器时，只会传入一个限定范围</p>
<p>例如：com.mlming 包下的所有类都要被获取，但是用户只会传一个“com.mlming”路径进来，这种情况下，我们怎么得到这个包下所有类的全限定名称呢？</p>
<p>在之前学反射时，我们学过，<strong>通过类加载器 是可以实现在任何环境下面都能获取到绝对路径的方法！</strong></p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java">之前学<span class="token class-name">Java</span><span class="token punctuation">,</span>我们都说过绝对路径不好<span class="token punctuation">,</span>因为换个机子就不对了
但是<span class="token punctuation">,</span>我们知道<span class="token punctuation">,</span>相对路径是为了无论什么环境都能移植
但是<span class="token class-name">Java</span>不是如此<span class="token punctuation">,</span>不同开发工具其的相对路径的默认相对的那个路径是不一样的
这就是失去了移植性
所以其实<span class="token class-name">Java</span>中一般也不用相对路径

在学<span class="token class-name">Node</span><span class="token punctuation">.</span>js时<span class="token punctuation">,</span>我们其实面临过一样的问题<span class="token punctuation">,</span>
我们最后采取的方式是<span class="token operator">:</span> 动态获取当前文件的绝对路径
这样就能无论放在那里都能动态获取到绝对路径<span class="token punctuation">,</span>从而实现了通用<span class="token punctuation">,</span>可移植

那么在<span class="token class-name">Java</span>中<span class="token punctuation">,</span>如何实现<span class="token operator">*</span><span class="token operator">*</span>动态获取绝对路径呢<span class="token operator">?</span><span class="token operator">*</span><span class="token operator">*</span>
以下代码是基本固定的<span class="token punctuation">,</span>以后<span class="token class-name">Java</span>使用路径<span class="token punctuation">,</span>基本上都是用这种方式<span class="token punctuation">,</span>所以最好背会<span class="token operator">:</span>
实现<span class="token operator">:</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getContextClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getResource</span><span class="token punctuation">(</span>文件相当于src的相对路径<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
注意<span class="token operator">:</span> 此种写法必须要求该文件处于<span class="token operator">*</span><span class="token operator">*</span>类路径下<span class="token punctuation">,</span>即src目录下<span class="token operator">*</span><span class="token operator">*</span>
     <span class="token operator">*</span><span class="token operator">*</span>从而<span class="token function">getSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span>里面填的是文件相当于src目录下的相对路径<span class="token operator">*</span><span class="token operator">*</span>
解析<span class="token operator">:</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span>获取当前线程对象
     <span class="token function">getContextClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span>是线程对象的一个方法<span class="token punctuation">,</span>获取到当前线程的类加载器对象
     <span class="token function">getResource</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 是类加载器对象的一个方法<span class="token punctuation">,</span>可以到当前类所在的根路径下加载资源
     <span class="token operator">*</span><span class="token operator">*</span>当前类所在的根路径 <span class="token operator">=</span><span class="token operator">></span> 类路径 <span class="token operator">=</span><span class="token operator">></span> src<span class="token operator">*</span><span class="token operator">*</span>
     所以这种方式要求文件必须位于src下<span class="token punctuation">,</span>是<span class="token function">getSource</span><span class="token punctuation">(</span><span class="token punctuation">)</span>的执行问题
     因为类的根目录肯定是src<span class="token punctuation">,</span>所以类所在根路径就是src<span class="token punctuation">,</span>所以资源即文件必须放在src下
     <span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span> 获取当前资源绝对路径<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<p>因此，<strong>我们就可以获得传入的指定路径的绝对路径了，获得绝对路径后，我们可以通过穷举的方式把里面的.class文件一一的获得，从而通过字符串分割等方式获得全限定名称部门，从而利用反射进行Class对象的创建，</strong>这样一来，就实现 <strong>获取指定范围下所有的Class对象</strong> 的需求了：</p>
<p>因为该功能是通用的，所以可以抽象成一个<strong>ClassUtil中的extractPackageClasses方法</strong>来进行实现：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Slf4j</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">ClassUtil</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">/**
     * 获取包下类集合
     *
     * @parampackageName包名
     * @return 类集合
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Set</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span> <span class="token function">extractPackageClasses</span><span class="token punctuation">(</span><span class="token class-name">String</span> packageName<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1) 获得类加载器:</span>
        <span class="token class-name">ClassLoader</span> classLoader <span class="token operator">=</span> <span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2) 通过类加载器加载指定路径的资源,获得其对应的绝对URL</span>
        <span class="token class-name">URL</span> url <span class="token operator">=</span> classLoader<span class="token punctuation">.</span><span class="token function">getResource</span><span class="token punctuation">(</span>packageName<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">.</span>&amp;quot<span class="token punctuation">;</span><span class="token punctuation">,</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token operator">/</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>url <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            log<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token class-name">FrameWorkConst</span><span class="token punctuation">.</span>PACKAGE_NOT_FOUND_ERROR_MSG <span class="token operator">+</span> packageName<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span>  <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// 3) 依据不同的资源类型，采用不同的方式获取资源的集合</span>
        <span class="token class-name">Set</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span> classSet <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token comment">// 过滤出文件类型的资源</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>url<span class="token punctuation">.</span><span class="token function">getProtocol</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">equalsIgnoreCase</span><span class="token punctuation">(</span><span class="token class-name">FrameWorkConst</span><span class="token punctuation">.</span>FILE_PROTOCOL<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            classSet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token class-name">File</span> packageDirectory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">File</span><span class="token punctuation">(</span>url<span class="token punctuation">.</span><span class="token function">getPath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token function">extractClassFile</span><span class="token punctuation">(</span>classSet<span class="token punctuation">,</span> packageDirectory<span class="token punctuation">,</span> packageName<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// TODO: 可以处理别的资源类型</span>

        <span class="token keyword">return</span> classSet<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 递归获取目标package里面的所有class文件(包括子package里的class文件)
     *
     * @param classSet 装载目标类的集合
     * @param packageDirectory    文件或者目录
     * @param packageName   包名
     * @return 类集合
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">extractClassFile</span><span class="token punctuation">(</span><span class="token class-name">Set</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span> classSet<span class="token punctuation">,</span> <span class="token class-name">File</span> packageDirectory<span class="token punctuation">,</span> <span class="token class-name">String</span> packageName<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 如果该packageDirectory是文件,则直接return不处理</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span>packageDirectory<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token comment">// 1) 获得目录下的所有的File,并对其进行过滤:</span>
        <span class="token comment">//    如果是目录,则还需要进一步的递归调用, 则加入到files数组, 然后之后遍历调用该函数进行穷举</span>
        <span class="token comment">//    如果是文件,且是.class文件,那么可以直接对其进行路径切割获得全限定名称,从而通过反射创建Class对象并加入set中</span>
        <span class="token class-name">File</span><span class="token punctuation">[</span><span class="token punctuation">]</span> files <span class="token operator">=</span> packageDirectory<span class="token punctuation">.</span><span class="token function">listFiles</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">FileFilter</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token annotation punctuation">@Override</span>
            <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">accept</span><span class="token punctuation">(</span><span class="token class-name">File</span> file<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>file<span class="token punctuation">.</span><span class="token function">isDirectory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">return</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
                <span class="token comment">// 如果是.class文件:</span>
                <span class="token class-name">String</span> absoluteFilePath <span class="token operator">=</span> file<span class="token punctuation">.</span><span class="token function">getAbsolutePath</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>absoluteFilePath<span class="token punctuation">.</span><span class="token function">endsWith</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token comment">// 进行路径切割,获得全限定名称:</span>
                    <span class="token comment">// 因为包名是.分割,而路径是/, 所以先替换</span>
                    absoluteFilePath <span class="token operator">=</span> absoluteFilePath<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span><span class="token class-name">File</span><span class="token punctuation">.</span>separator<span class="token punctuation">,</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">.</span>&amp;quot<span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">// 从包名处截断, 一直到最后一个.的位置, 从而就可以得到包名.类名, 也就是全限定名称</span>
                    <span class="token class-name">String</span> className <span class="token operator">=</span> absoluteFilePath<span class="token punctuation">.</span><span class="token function">substring</span><span class="token punctuation">(</span>absoluteFilePath<span class="token punctuation">.</span><span class="token function">indexOf</span><span class="token punctuation">(</span>packageName<span class="token punctuation">)</span><span class="token punctuation">,</span>absoluteFilePath<span class="token punctuation">.</span><span class="token function">lastIndexOf</span><span class="token punctuation">(</span><span class="token operator">&amp;</span>quot<span class="token punctuation">;</span><span class="token punctuation">.</span>&amp;quot<span class="token punctuation">;</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">// 反射获得Class对象, 并把该Class对象加入到set中去</span>
                    <span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span> clazz <span class="token operator">=</span> <span class="token function">loadClass</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    classSet<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
                <span class="token keyword">return</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 2) 把files数组里面存的这些目录, 进一步递归调用</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token operator">!</span><span class="token class-name">ValidationUtil</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>files<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">File</span> file <span class="token operator">:</span> files<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token function">extractClassFile</span><span class="token punctuation">(</span>classSet<span class="token punctuation">,</span>file<span class="token punctuation">,</span>packageName<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 获得类加载器
     * @return
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">ClassLoader</span> <span class="token function">getClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">currentThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getContextClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 获取Class对象
     *
     * @param className class全名=package + 类名
     * @return Class
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">Class</span><span class="token operator">&amp;</span>lt<span class="token punctuation">;</span><span class="token operator">?</span><span class="token operator">&amp;</span>gt<span class="token punctuation">;</span> <span class="token function">loadClass</span><span class="token punctuation">(</span><span class="token class-name">String</span> className<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">try</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">return</span> <span class="token class-name">Class</span><span class="token punctuation">.</span><span class="token function">forName</span><span class="token punctuation">(</span>className<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">ClassNotFoundException</span> e<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            log<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span><span class="token class-name">FrameWorkConst</span><span class="token punctuation">.</span>LOAD_CLASS_ERROR_MSG<span class="token punctuation">,</span> e<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>e<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
</li>
<li><p>上一步，我们通过ClassUtil中的extractPackageClasses方法就可以获得指定包下面的所有Class对象了，现在我们要做的，就是要定位并提取</p>
<p>但是这个提取目标，是和IOC容器本身的实现有关的，所以我们把这一步放到容器实现时去实现。</p>
</li>
</ul>
<h3 id="实现容器：实现IOC容器的-“容器”"><a href="#实现容器：实现IOC容器的-“容器”" class="headerlink" title="实现容器：实现IOC容器的 “容器”"></a>实现容器：实现IOC容器的 “容器”</h3><ul>
<li><p>要求：</p>
<ul>
<li><p>因为一个框架只能有一个IOC容器，所以该容器类必须是<strong>单例模式</strong>实现的</p>
<p>因为 <strong>普通的饿汉模式</strong> 以及 <strong>双重检查锁的懒汉模式</strong> 都会被 <strong>反射</strong> 破坏其单例性！</p>
<p>所以，此处我们选用 <strong>枚举饿汉模式</strong> 来实现单例， <strong>由于枚举的性质，可以帮我们抵御反射以及序列化的破坏</strong>。</p>
<p><strong>（之所以是懒汉模式，是因为IOC容器应该是项目启动时就被创建的，符合懒汉模式）</strong></p>
</li>
<li><p><strong>容器的数据结构（载体）：</strong></p>
<p>因为在IOC容器里面，我们要得到一个实例对象，怎么获得？很明显需要一个唯一标识来获取，也就是说这应该是Key-Value形式</p>
<p><strong>以Class对象作为Key（因为一个类的Class对象在JVM中只有一个），以实例对象作为Value。</strong></p>
<p>因此，<strong>毫无疑问应该是一个Map数据结构，考虑到多线程安全问题，我们可以使用ConcurrentHashMap</strong></p>
</li>
</ul>
</li>
<li><p>实现流程：</p>
<ul>
<li><p>定义容器类：通过  <strong>枚举饿汉模式</strong> 来定义 IOC容器类，其中以 <strong>ConcurrentHashMap</strong>作为数据载体</p>
</li>
<li><p>实现容器的加载：即<strong>1.3后面定位与提取目标的那一个流程</strong></p>
<ul>
<li><p>通过1.3中的extractPackageClasses方法来获得指定范围下所有的类的集合</p>
</li>
<li><p>通过遍历该集合，并通过isAnnotationPresent判断其是否是被 我们创建的注解 所标记的， </p>
<p>如果是，那这就是目标，因此要把其实例化，把Class对象以及实例化对象放入ConcurrentHashMap中</p>
</li>
</ul>
</li>
<li><p>对外提供一些操作供外界操作该容器</p>
<ul>
<li><strong>增加、删除操作</strong></li>
<li><strong>根据Class获取对应实例</strong></li>
<li><strong>获取多有的Class和实例</strong></li>
<li><strong>通过注解来获取被注解标注的Class</strong></li>
<li><strong>通过超类获取对应的子类Class</strong></li>
<li><strong>获取容器载体保存Class的数量</strong></li>
</ul>
</li>
</ul>
</li>
<li><p>具体实现：</p>
<p>BeanContainer类：</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
 * 容器类
 */</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">BeanContainer</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">/**
     * 存放所有被配置标记的目标对象的Map
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">final</span> <span class="token class-name">ConcurrentHashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">,</span><span class="token class-name">Object</span><span class="token punctuation">></span></span> beanMap <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ConcurrentHashMap</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">/**
     * 加载bean的注解列表
     */</span>
    <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">Annotation</span><span class="token punctuation">></span><span class="token punctuation">></span></span> BEAN_ANNOTATIONS
            <span class="token operator">=</span> <span class="token class-name">Arrays</span><span class="token punctuation">.</span><span class="token function">asList</span><span class="token punctuation">(</span><span class="token class-name">Component</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token class-name">Controller</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token class-name">Service</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token class-name">Repository</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

    <span class="token comment">/**
     * 枚举懒汉单例模式 实现 BeanContainer的获取
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">BeanContainer</span>  <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token class-name">ContainerHolder</span><span class="token punctuation">.</span>HOLDER<span class="token punctuation">.</span>instance<span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token keyword">private</span> <span class="token keyword">enum</span> <span class="token class-name">ContainerHolder</span> <span class="token punctuation">&#123;</span>
        HOLDER<span class="token punctuation">;</span>
        <span class="token keyword">private</span> <span class="token class-name">BeanContainer</span> instance<span class="token punctuation">;</span>
        <span class="token comment">// 懒汉模式:</span>
        <span class="token class-name">ContainerHolder</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">BeanContainer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">// 加载标记: 一个容器只会被加载一次</span>
    <span class="token keyword">private</span> <span class="token keyword">boolean</span> loaded <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token keyword">boolean</span> <span class="token function">isLoaded</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span> <span class="token keyword">return</span> loaded<span class="token punctuation">;</span> <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 加载指定范围下面的被注解标记的目标Bean
     *
     * 因为只能加载一次,所以直接用synchronized实现比较方便,当然也能用双重验证锁
     * @param packageName
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">synchronized</span> <span class="token keyword">void</span> <span class="token function">loadBeans</span><span class="token punctuation">(</span><span class="token class-name">String</span> packageName<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1) 如果加载过了, 则直接return</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>loaded<span class="token punctuation">)</span> <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token comment">// 2) 通过ClassUtil.extractPackageClasses()获得指定包下所有类对象</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> classes <span class="token operator">=</span> <span class="token class-name">ClassUtil</span><span class="token punctuation">.</span><span class="token function">extractPackageClasses</span><span class="token punctuation">(</span>packageName<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 3) 遍历set, 对每一个Class判断其是否被 我们创建的注解修饰, 如果是, 则要注册到容器中</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz <span class="token operator">:</span> classes<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">Annotation</span><span class="token punctuation">></span></span> annotation <span class="token operator">:</span> BEAN_ANNOTATIONS<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>clazz<span class="token punctuation">.</span><span class="token function">isAnnotationPresent</span><span class="token punctuation">(</span>annotation<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    beanMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>clazz<span class="token punctuation">,</span><span class="token class-name">ClassUtil</span><span class="token punctuation">.</span><span class="token function">newInstance</span><span class="token punctuation">(</span>clazz<span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// 4) 加载完毕,设置为true</span>
        loaded <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 添加一个class对象及其Bean实例
     *
     * @param clazz Class对象
     * @param bean  Bean实例
     * @return 原有的Bean实例, 没有则返回null
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">addBean</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz<span class="token punctuation">,</span> <span class="token class-name">Object</span> bean<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> beanMap<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>clazz<span class="token punctuation">,</span> bean<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 移除一个IOC容器管理的对象
     *
     * @param clazz Class对象
     * @return 删除的Bean实例, 没有则返回null
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">removeBean</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> beanMap<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 根据Class对象获取Bean实例
     *
     * @param clazz Class对象
     * @return Bean实例
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Object</span> <span class="token function">getBean</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> beanMap<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 获取容器管理的所有Class对象集合
     *
     * @return Class集合
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">getClasses</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> beanMap<span class="token punctuation">.</span><span class="token function">keySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 获取所有Bean集合
     *
     * @return Bean集合
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Object</span><span class="token punctuation">></span></span> <span class="token function">getBeans</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span> beanMap<span class="token punctuation">.</span><span class="token function">values</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 根据注解筛选出Bean的Class集合
     *
     * @param annotation 注解
     * @return Class集合
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">getClassesByAnnotation</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span> <span class="token keyword">extends</span> <span class="token class-name">Annotation</span><span class="token punctuation">></span></span> annotation<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token comment">//1.获取beanMap的所有class对象</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> keySet <span class="token operator">=</span> <span class="token function">getClasses</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token class-name">ValidationUtil</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>keySet<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">//2.通过注解筛选被注解标记的class对象，并添加到classSet里</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> classSet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz <span class="token operator">:</span> keySet<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            <span class="token comment">//类是否有相关的注解标记</span>
            <span class="token keyword">if</span><span class="token punctuation">(</span>clazz<span class="token punctuation">.</span><span class="token function">isAnnotationPresent</span><span class="token punctuation">(</span>annotation<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
                classSet<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">return</span> classSet<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token operator">?</span> classSet<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    <span class="token comment">/**
     * 通过接口或者父类获取实现类或者子类的Class集合，不包括其本身
     *
     * @param interfaceOrClass 接口Class或者父类Class
     * @return Class集合
     */</span>
    <span class="token keyword">public</span> <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> <span class="token function">getClassesBySuper</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> interfaceOrClass<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
        <span class="token comment">//1.获取beanMap的所有class对象</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> keySet <span class="token operator">=</span> <span class="token function">getClasses</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token class-name">ValidationUtil</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>keySet<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">//2.判断keySet里的元素是否是传入的接口或者类的子类，如果是，就将其添加到classSet里</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> classSet <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">HashSet</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">></span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span><span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz <span class="token operator">:</span> keySet<span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
            <span class="token comment">//判断keySet里的元素是否是传入的接口或者类的子类</span>
            <span class="token keyword">if</span><span class="token punctuation">(</span>interfaceOrClass<span class="token punctuation">.</span><span class="token function">isAssignableFrom</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span> <span class="token operator">&amp;&amp;</span> <span class="token operator">!</span>clazz<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>interfaceOrClass<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">&#123;</span>
                classSet<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">return</span> classSet<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">></span> <span class="token number">0</span><span class="token operator">?</span> classSet<span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
    
    <span class="token comment">/**
     * 获得注册Bean个数
     * @return
     */</span>
    <span class="token keyword">public</span> <span class="token keyword">int</span> <span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token keyword">return</span> beanMap<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li>
</ul>
<h3 id="实现依赖注入：实现IOC容器的“IOC”"><a href="#实现依赖注入：实现IOC容器的“IOC”" class="headerlink" title="实现依赖注入：实现IOC容器的“IOC”"></a>实现依赖注入：实现IOC容器的“IOC”</h3><ul>
<li><p>通过上面的容器实现后，我们其实已经把之前创建的注解（@Controller、Service、Repository、Component）标注的类对应的Bean都注册到容器里面了</p>
<p>但是当我们跑单例点开里面的一个个实例对象就会发现，内部的成员变量还是null，</p>
<p>也就是说：<strong>我们还需要实现依赖注入，来实现给Bean的成员变量进行注入</strong></p>
</li>
<li><p>实现流程：</p>
<ul>
<li>创建注解：模仿前面我们实现IOC容器的“我们需要一个注解来标注什么类需要被注册”，我们肯定也需要一个注解来标记什么成员变量需要被注入</li>
<li>定位目标（成员变量）：我们要从遍历容器，同时取出每一个Class对象里面的Field对象，然后通过isAnnotationPresent判断是否是上述注解标注，如果是就说明要被注入</li>
<li>实现依赖注入：获得该Field对象的类型，如果容器中有该Class对象，则直接注入其实例对象；如果没有，说明该类型是接口或没被之前那些注解修饰的父类，所以此时要通过容器对外提供的getClassesBySuper方法获得其实现类，如果只有一个则直接注入，否则还要通过@Qualifier的值进行判断应该取该类型的哪一个实现类。之后通过Field对象的set方法，来实现对对应成员变量的赋值，也就是注入</li>
</ul>
</li>
<li><p>具体实现：</p>
<ul>
<li>创建注解：</li>
</ul>
<p>@Autowired</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span><span class="token class-name">ElementType</span><span class="token punctuation">.</span>FIELD<span class="token punctuation">)</span>
<span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span><span class="token class-name">RetentionPolicy</span><span class="token punctuation">.</span>RUNTIME<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@interface</span> <span class="token class-name">Autowired</span> <span class="token punctuation">&#123;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>

<p>@Qualifier</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
 * 用于当@Autowired修饰的成员变量的类型下面有多个实现类,用于特别注明该实现类的类名, 从而确定选哪一个作为该成员变量的注入
 */</span>
<span class="token annotation punctuation">@Target</span><span class="token punctuation">(</span><span class="token class-name">ElementType</span><span class="token punctuation">.</span>FIELD<span class="token punctuation">)</span>
<span class="token annotation punctuation">@Retention</span><span class="token punctuation">(</span><span class="token class-name">RetentionPolicy</span><span class="token punctuation">.</span>RUNTIME<span class="token punctuation">)</span>
<span class="token keyword">public</span> <span class="token annotation punctuation">@interface</span> <span class="token class-name">Qualifier</span> <span class="token punctuation">&#123;</span>
    <span class="token class-name">String</span> <span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">default</span> <span class="token string">""</span><span class="token punctuation">;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>

<ul>
<li><p>实现依赖注入的类:</p>
<p>DependencyInjector类:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">/**
 * 实现依赖注入的类
 */</span>
<span class="token annotation punctuation">@Slf4j</span>
<span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">DependencyInjector</span> <span class="token punctuation">&#123;</span>
    <span class="token comment">// Bean容器</span>
    <span class="token keyword">private</span> <span class="token class-name">BeanContainer</span> beanContainer<span class="token punctuation">;</span>
    <span class="token keyword">public</span> <span class="token class-name">DependencyInjector</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        beanContainer <span class="token operator">=</span> <span class="token class-name">BeanContainer</span><span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     *  执行IOC:
    */</span>
    <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">doIOC</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 0) 如果beanContainer里面没有数据,则无需执行IOC</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>beanContainer<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            log<span class="token punctuation">.</span><span class="token function">warn</span><span class="token punctuation">(</span><span class="token class-name">FrameWorkConst</span><span class="token punctuation">.</span>EMPTY_BEAN_CONTAINER_ERROR_MSG<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">return</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// 1) 遍历beanContainer中所有被注册进去的类</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> clazz <span class="token operator">:</span> beanContainer<span class="token punctuation">.</span><span class="token function">getClasses</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token comment">// 2) 对于每一个class, 获得其所有成员变量,</span>
            <span class="token class-name">Field</span><span class="token punctuation">[</span><span class="token punctuation">]</span> fields <span class="token operator">=</span> clazz<span class="token punctuation">.</span><span class="token function">getDeclaredFields</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token class-name">ValidationUtil</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>fields<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token keyword">continue</span><span class="token punctuation">;</span>
            <span class="token comment">// 3) 对每一个成员变量Field判断是否由@Autowired修饰, 如果是,说明要被注入</span>
            <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Field</span> field <span class="token operator">:</span> fields<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">if</span><span class="token punctuation">(</span>field<span class="token punctuation">.</span><span class="token function">isAnnotationPresent</span><span class="token punctuation">(</span><span class="token class-name">Autowired</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                    <span class="token comment">// 4) 获得该field的注入对象</span>
                    <span class="token class-name">Object</span> filedObject <span class="token operator">=</span> <span class="token function">getFieldInstance</span><span class="token punctuation">(</span>field<span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token comment">// 5) 之后通过Field对象的set方法，来实现对对应成员变量的赋值，也就是注入</span>
                    <span class="token comment">// 如果此时filedObject还是null, 说明注入失败, 应该报错</span>
                    <span class="token keyword">if</span><span class="token punctuation">(</span>filedObject <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                        <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span><span class="token string">"unable to inject relevant type，Object is: "</span> <span class="token operator">+</span> beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">" target fieldClass is:"</span> <span class="token operator">+</span> field<span class="token punctuation">.</span><span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token string">" fieldName is : "</span> <span class="token operator">+</span> field<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                    <span class="token punctuation">&#125;</span>
                    <span class="token class-name">ClassUtil</span><span class="token punctuation">.</span><span class="token function">setField</span><span class="token punctuation">(</span>field<span class="token punctuation">,</span>beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>clazz<span class="token punctuation">)</span><span class="token punctuation">,</span>filedObject<span class="token punctuation">,</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
                <span class="token punctuation">&#125;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
    <span class="token punctuation">&#125;</span>

    <span class="token comment">/**
     * 获得field的注入对象
     * @param field
     * @return
     */</span>
    <span class="token keyword">private</span> <span class="token class-name">Object</span> <span class="token function">getFieldInstance</span><span class="token punctuation">(</span><span class="token class-name">Field</span> field<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
        <span class="token comment">// 1) 获得该Field对象的类型，如果容器中有该Class对象，则直接注入其实例对象；</span>
        <span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> type <span class="token operator">=</span> field<span class="token punctuation">.</span><span class="token function">getType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">null</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">return</span> beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// 2) 如果没有，说明该类型是接口或没被之前那些注解修饰的父类，</span>
        <span class="token comment">//    所以此时要通过容器对外提供的getClassesBySuper方法获得其实现类集合</span>
        <span class="token class-name">Set</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Class</span><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span><span class="token punctuation">></span></span> classesBySuper <span class="token operator">=</span> beanContainer<span class="token punctuation">.</span><span class="token function">getClassesBySuper</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 3) 如果只有一个实现类, 则直接注入</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>classesBySuper<span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
           <span class="token keyword">return</span> beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>classesBySuper<span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">next</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token comment">// 4) 否则还要通过@Qualifier的值进行判断应该取该类型的哪一个实现类。</span>
        <span class="token class-name">Qualifier</span> qualifier <span class="token operator">=</span> field<span class="token punctuation">.</span><span class="token function">getAnnotation</span><span class="token punctuation">(</span><span class="token class-name">Qualifier</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token comment">// 如果此时该field没有被@Qualifier标注,或者标注了但没有赋值, 是不合法的, 因为有多个实现类, 所以要报错</span>
        <span class="token keyword">if</span><span class="token punctuation">(</span>qualifier <span class="token operator">==</span> <span class="token keyword">null</span> <span class="token operator">||</span> <span class="token class-name">ValidationUtil</span><span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span>qualifier<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">RuntimeException</span><span class="token punctuation">(</span>type <span class="token operator">+</span> <span class="token class-name">FrameWorkConst</span><span class="token punctuation">.</span>MULTIPLE_IMPLEMENTS_BUT_NOT_SET_QUALIFIER_ERROR_MSG<span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token class-name">String</span> qualifierName <span class="token operator">=</span> qualifier<span class="token punctuation">.</span><span class="token function">value</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
        <span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token class-name">Class</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token operator">?</span><span class="token punctuation">></span></span> aClass <span class="token operator">:</span> classesBySuper<span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
            <span class="token keyword">if</span><span class="token punctuation">(</span>qualifierName<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>aClass<span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">&#123;</span>
                <span class="token keyword">return</span> beanContainer<span class="token punctuation">.</span><span class="token function">getBean</span><span class="token punctuation">(</span>aClass<span class="token punctuation">)</span><span class="token punctuation">;</span>
            <span class="token punctuation">&#125;</span>
        <span class="token punctuation">&#125;</span>
        <span class="token keyword">return</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
    <span class="token punctuation">&#125;</span>
<span class="token punctuation">&#125;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre></li>
</ul>
</li>
</ul>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结:"></a>总结:</h3><p><img src="/myblog/%5CmdImage%5Cimage-20220604101411673.png" alt="image-20220604101411673"></p>
<p>至此，我们的IOC容器就实现完毕了，总结而言分为了下面三个主要部分：</p>
<ul>
<li><p><strong>注解</strong>：实现容器：@Controller、@Service、@Repository、@Component、实现依赖注入：@Autowired、@Qualifier</p>
</li>
<li><p><strong>BeanContainer类：</strong>容器类，由 <strong>枚举懒汉单例模式 + ConcurrentHashMap&lt;Class,Object&gt;</strong> 并结合 <strong>反射 + 注解</strong> 实现，<strong>是IOC容器的 “容器” 的实现</strong></p>
</li>
<li><p><strong>DependencyInjector类</strong>：实现依赖注入的处理类，结合 <strong>反射 + 注解</strong> 实现， <strong>是IOC容器的 “IOC” 的实现</strong></p>
</li>
</ul>
<p>同时在实现IOC容器过程中，为了辅助我们功能的实现，我们来创建了一个ClassUtil工具类，帮助我们实现关于类相关的功能：</p>
<ul>
<li><p><strong>extractPackageClasses方法</strong>：获得指定范围（包）下的所有的.class文件对应的Class对象</p>
</li>
<li><p>一些反射的封装,例如newInstance实例化对象、loadClass获得Class对象等</p>
</li>
</ul>
<p>同时最重要的，IOC容器本身实现了，<strong>我们怎么去实现IOC容器的初始化呢？或者说，怎么去启动IOC容器去执行其作用呢？</strong></p>
<p>我们只需要 <strong>在某个满足项目启动时，其就会被加载的类的方法中 (例如DispatcherServlet是一个servlet，其init方法会在项目启动时被自动调用)</strong> 执行以下代码:</p>
<pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token comment">// 1) 先把容器给加载了</span>
<span class="token class-name">BeanContainer</span><span class="token punctuation">.</span><span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">loadBeans</span><span class="token punctuation">(</span><span class="token string">"指定包名"</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 2) 再进行依赖注入</span>
<span class="token keyword">new</span> <span class="token class-name">DependencyInjector</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">doIOC</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre>


                
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="/myblog/about" rel="external nofollow noreferrer">Lin MingMing</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://mlming.gitee.io/myblog/myblog/2022/06/04/simplespring-kai-fa-ioc-rong-qi-de-she-ji-yu-shi-xian-3/">https://mlming.gitee.io/myblog/myblog/2022/06/04/simplespring-kai-fa-ioc-rong-qi-de-she-ji-yu-shi-xian-3/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="/myblog/about" target="_blank">Lin MingMing</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/myblog/tags/%E5%90%8E%E7%AB%AF/">
                                    <span class="chip bg-color">后端</span>
                                </a>
                            
                                <a href="/myblog/tags/%E9%80%A0%E8%BD%AE%E5%AD%90/">
                                    <span class="chip bg-color">造轮子</span>
                                </a>
                            
                                <a href="/myblog/tags/Spring/">
                                    <span class="chip bg-color">Spring</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/myblog/libs/share/css/share.min.css">
<div id="article-share">

    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/myblog/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
        </div>
    </div>

    

    

    

    

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/myblog/2022/06/05/zuo-ti-xin-de-fang-fa-lun-lian-biao-cao-zuo-4/">
                    <div class="card-image">
                        
                        
                        <img src="/myblog/medias/featureimages/22.jpg" class="responsive-img" alt="做题心得-方法论-链表操作(4)">
                        
                        <span class="card-title">做题心得-方法论-链表操作(4)</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2022-06-05
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/myblog/categories/%E5%81%9A%E9%A2%98%E5%BF%83%E5%BE%97%E4%B8%93%E6%A0%8F/" class="post-category">
                                    做题心得专栏
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/myblog/tags/%E7%AE%97%E6%B3%95/">
                        <span class="chip bg-color">算法</span>
                    </a>
                    
                    <a href="/myblog/tags/%E9%93%BE%E8%A1%A8/">
                        <span class="chip bg-color">链表</span>
                    </a>
                    
                    <a href="/myblog/tags/%E5%8F%8C%E6%8C%87%E9%92%88/">
                        <span class="chip bg-color">双指针</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/myblog/2022/06/04/zuo-ti-xin-de-fang-fa-lun-shu-zu-cao-zuo-3/">
                    <div class="card-image">
                        
                        
                        <img src="/myblog/medias/featureimages/7.jpg" class="responsive-img" alt="做题心得-方法论-数组操作(3)">
                        
                        <span class="card-title">做题心得-方法论-数组操作(3)</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2022-06-04
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/myblog/categories/%E5%81%9A%E9%A2%98%E5%BF%83%E5%BE%97%E4%B8%93%E6%A0%8F/" class="post-category">
                                    做题心得专栏
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/myblog/tags/%E7%AE%97%E6%B3%95/">
                        <span class="chip bg-color">算法</span>
                    </a>
                    
                    <a href="/myblog/tags/%E6%95%B0%E7%BB%84/">
                        <span class="chip bg-color">数组</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/myblog/libs/codeBlock/codeBlockFuction.js"></script>


  <!-- 是否加载使用自带的 prismjs. -->
  <script type="text/javascript" src="/myblog/libs/prism/prism.min.js"></script>


<!-- 代码语言 -->

<script type="text/javascript" src="/myblog/libs/codeBlock/codeLang.js"></script>


<!-- 代码块复制 -->

<script type="text/javascript" src="/myblog/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/myblog/libs/codeBlock/codeShrink.js"></script>



    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget card" style="background-color: white;">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/myblog/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            collapseDepth: Number('0'),
            headingSelector: 'h2, h3, h4'
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>




    <footer class="page-footer bg-color">
    

    <div class="container row center-align"
         style="margin-bottom: 0px !important;">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            
                <span id="year">2022</span>
            
            <a href="/myblog/about" target="_blank">Lin MingMing</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
                &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                        class="white-color">31.5k</span>
            
            
            
                
            
            
                <span id="busuanzi_container_site_pv">
                &nbsp;|&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;
                    <span id="busuanzi_value_site_pv" class="white-color"></span>
            </span>
            
            
                <span id="busuanzi_container_site_uv">
                &nbsp;|&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;
                    <span id="busuanzi_value_site_uv" class="white-color"></span>
            </span>
            
            <br>

            <!-- 运行天数提醒. -->
            
            <br>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://gitee.com/mmwindy" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:1139380762@qq.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>







    <a href="tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=1139380762" class="tooltipped" target="_blank" data-tooltip="QQ联系我: 1139380762" data-position="top" data-delay="50">
        <i class="fab fa-qq"></i>
    </a>







</div>
    </div>
</footer>

<div class="progress-bar"></div>


    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script type="text/javascript">
$(function () {
    var searchFunc = function (path, search_id, content_id) {
        'use strict';
        $.ajax({
            url: path,
            dataType: "xml",
            success: function (xmlResponse) {
                // get the contents from search data
                var datas = $("entry", xmlResponse).map(function () {
                    return {
                        title: $("title", this).text(),
                        content: $("content", this).text(),
                        url: $("url", this).text()
                    };
                }).get();
                var $input = document.getElementById(search_id);
                var $resultContent = document.getElementById(content_id);
                $input.addEventListener('input', function () {
                    var str = '<ul class=\"search-result-list\">';
                    var keywords = this.value.trim().toLowerCase().split(/[\s\-]+/);
                    $resultContent.innerHTML = "";
                    if (this.value.trim().length <= 0) {
                        return;
                    }
                    // perform local searching
                    datas.forEach(function (data) {
                        var isMatch = true;
                        var data_title = data.title.trim().toLowerCase();
                        var data_content = data.content.trim().replace(/<[^>]+>/g, "").toLowerCase();
                        var data_url = data.url;
                        data_url = data_url.indexOf('/') === 0 ? data.url : '/' + data_url;
                        var index_title = -1;
                        var index_content = -1;
                        var first_occur = -1;
                        // only match artiles with not empty titles and contents
                        if (data_title !== '' && data_content !== '') {
                            keywords.forEach(function (keyword, i) {
                                index_title = data_title.indexOf(keyword);
                                index_content = data_content.indexOf(keyword);
                                if (index_title < 0 && index_content < 0) {
                                    isMatch = false;
                                } else {
                                    if (index_content < 0) {
                                        index_content = 0;
                                    }
                                    if (i === 0) {
                                        first_occur = index_content;
                                    }
                                }
                            });
                        }
                        // show search results
                        if (isMatch) {
                            str += "<li><a href='" + data_url + "' class='search-result-title'>" + data_title + "</a>";
                            var content = data.content.trim().replace(/<[^>]+>/g, "");
                            if (first_occur >= 0) {
                                // cut out 100 characters
                                var start = first_occur - 20;
                                var end = first_occur + 80;
                                if (start < 0) {
                                    start = 0;
                                }
                                if (start === 0) {
                                    end = 100;
                                }
                                if (end > content.length) {
                                    end = content.length;
                                }
                                var match_content = content.substr(start, end);
                                // highlight all keywords
                                keywords.forEach(function (keyword) {
                                    var regS = new RegExp(keyword, "gi");
                                    match_content = match_content.replace(regS, "<em class=\"search-keyword\">" + keyword + "</em>");
                                });

                                str += "<p class=\"search-result\">" + match_content + "...</p>"
                            }
                            str += "</li>";
                        }
                    });
                    str += "</ul>";
                    $resultContent.innerHTML = str;
                });
            }
        });
    };

    searchFunc('/myblog/search.xml', 'searchInput', 'searchResult');
});
</script>

    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/myblog/libs/materialize/materialize.min.js"></script>
    <script src="/myblog/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/myblog/libs/aos/aos.js"></script>
    <script src="/myblog/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/myblog/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/myblog/js/matery.js"></script>

    

    

    <!-- 雪花特效 -->
    

    <!-- 鼠标星星特效 -->
     
        <script type="text/javascript">
            // 只在桌面版网页启用特效
            var windowWidth = $(window).width();
            if (windowWidth > 768) {
                document.write('<script type="text/javascript" src="/myblog/libs/others/star.js"><\/script>');
            }
        </script>
    

    

    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/myblog/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/myblog/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    <!--腾讯兔小巢-->
    
    

    

    

    
    <script src="/myblog/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
